<?php

namespace app\helper\Websocket;

use app\atrait\Single;
use app\helper\AgoraDynamic\RtcTokenBuilder;
use app\model\VoiceChatRoom;
use app\model\VoiceChatUser;
use GatewayWorker\Lib\Gateway;
use support\Redis;

class VoiceChat
{

    use Single;


    /**
     * 修改最先进入房间的人为房主
     *
     * @param $room
     * @param string $excludeUser
     * @return void
     */
    protected function firstOnlineMemberToBeOwner($room, string $excludeUser = ''): void
    {

        // 检查房间是否有房主
        $roomOwnerInfo = VoiceChatUser::query()->where('room', $room)->where('type', 1)->where('state', 1)->first();

        if (!$roomOwnerInfo || !empty($excludeUser)) {
            // 修改最先加入的人为房主
            $firstOnlineUserInfo = VoiceChatUser::query()->where('room', $room)->whereNot('ulid', $excludeUser)->where('state', 1)->oldest('join_at')->first();
            if ($firstOnlineUserInfo) {
                $firstOnlineUserInfo->update([
                    'type' => 1,
                ]);
                // 房主变更通知
                Gateway::sendToGroup(VoiceChat::getInstance()->getRoomName($room), [
                    'scene' => 'voice-chat',
                    'event' => 'group-modify-owner',
                    'data' => [
                        'user' => $firstOnlineUserInfo->user,
                        'member' => (string)$firstOnlineUserInfo->ulid,
                        'last_owner' => $excludeUser ?? '',
                    ]
                ]);
                // 成为房主通知
                Gateway::sendToUid($firstOnlineUserInfo->user, [
                    'scene' => 'voice-chat',
                    'event' => 'member-become-owner',
                ]);
            }
        }

    }


    /**
     * 收到消息
     *
     * @param $client_id
     * @param $data
     * @return void
     */
    public function OnMessage($client_id, $data): void
    {

        // 检查用户是否在线
        if (!Gateway::isOnline($client_id)) {
            Gateway::sendToClient($client_id, [
                'scene' => 'voice-chat',
                'event' => 'client-error-tip',
                'data' => [
                    'message' => '请先登录',
                ]
            ]);
            return;
        }

        $userId = Gateway::getUidByClientId($client_id);

        $joinRoomPosition = 3;  // 加入房间身份 1房主 2管理员 3普通成员
        $createRoom = false;  // 是否创建房间
        $voiceChatUserInfo = null;
        if($data['event'] === 'join-room') { // 创建房间
            if(empty($data['data']['key']) || empty($data['data']['type'])) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '缺少必要参数',
                    ]
                ]);
                return;
            }
            $roomInfo = VoiceChatRoom::query()->where('key', $data['data']['key'])->where('type', $data['data']['type'])->where('state', 1)->first();
            if(!$roomInfo) {
                $roomInfo = VoiceChatRoom::query()->create([
                    'key' => $data['data']['key'],
                    'type' => $data['data']['type'],
                    'user' => $userId,
                    'gag_type' => 0,
                    'state' => 1,
                ]);

                $joinRoomPosition = 1;  // 房主
                $createRoom = true;
            }
        } else {
            $voiceChatUserInfo = VoiceChatUser::query()->where('ulid', $data['data']['member'])->where('user', $userId)->first();
            if (!$voiceChatUserInfo) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '请先加入房间',
                    ]
                ]);
                return;
            }

            $roomInfo = VoiceChatRoom::query()->where('ulid', $voiceChatUserInfo->room)->where('state', 1)->first();
            if (!$roomInfo) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '房间不存在',
                    ]
                ]);
                return;
            }

            // 清理已离开房间的成员
            $offlineMe = false;
            $onlineUserList = Gateway::getUidListByGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid));
            $offlineMemberList = VoiceChatUser::query()->where('room', $roomInfo->ulid)->where('state', 1)->whereNotIn('user', array_values($onlineUserList))->get();
            if (count($offlineMemberList) > 0) {
                // 清理离线成员通知
                Gateway::sendToGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid), [
                    'scene' => 'voice-chat',
                    'event' => 'group-clear-offline-member',
                ]);
            }
            foreach ($offlineMemberList as $offlineMember) {
                // 被踢出通知
                Gateway::sendToUid($offlineMember->user, [
                    'scene' => 'voice-chat',
                    'event' => 'member-kick',
                ]);
                $offlineMember->update([
                    'state' => 0,
                ]);
                if ($offlineMember->ulid === $voiceChatUserInfo->ulid) {
                    $offlineMe = true;
                }
            }

            // 如果我被踢出房间
            if ($offlineMe || $voiceChatUserInfo->state !== 1) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '请先加入房间',
                    ]
                ]);
                return;
            }

            // 检查更新房主
            $this->firstOnlineMemberToBeOwner($roomInfo->ulid);
        }


        /**
         * 事件处理
         */
        // 加入房间
        if($data['event'] === 'join-room') {
            $roomOwnerInfo = VoiceChatUser::query()->where('room', $roomInfo->ulid)->where('type', 1)->where('state', 1)->first();
            // 检查房间是否有房主
            if (!$roomOwnerInfo) {
                $joinRoomPosition = 1;  // 房主
            } else {
                // 如果有超级权限，则自动成为房主
                if ($data['data']['is_super'] == 1) {
                    $roomOwnerInfo->update([
                        'type' => 3,
                    ]);
                    $joinRoomPosition = 1;
                }
            }

            // 加入房间
            $memberInfo = VoiceChatUser::query()->updateOrCreate([
                'room' => $roomInfo->ulid,
                'user' => $userId,
            ], [
                'user_data' => [
                    'role_id' => $data['data']['role_id'],
                    'role_name' => $data['data']['role_name'],
                    'level' => $data['data']['level'],
                    'occupation' => $data['data']['occupation'],
                ],
                'type' => $joinRoomPosition,
                'join_at' => date('Y-m-d H:i:s'),
                'state' => 1,
            ]);

            Gateway::joinGroup($client_id, VoiceChat::getInstance()->getRoomName($roomInfo->ulid));

            // 获取声网配置
            $appId = getenv('AGORA_APP_ID');
            $appCertificate = getenv('AGORA_APP_CERTIFICATE');
            $tokenExpirationInSeconds = 115200;  // 解决时区提前八小时 24+8
            $privilegeExpirationInSeconds = 115200;  // 解决时区提前八小时 24+8

            $token = RtcTokenBuilder::buildTokenWithUid($appId, $appCertificate, VoiceChat::getInstance()->getRoomName($roomInfo->ulid), $memberInfo->ulid, RtcTokenBuilder::ROLE_PUBLISHER, $tokenExpirationInSeconds, $privilegeExpirationInSeconds);

            // 进入房间通知
            Gateway::sendToGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid), [
                'scene' => 'voice-chat',
                'event' => 'group-join-room',
                'data' => [
                    'user' => $memberInfo->user,
                    'member' => [
                        'ulid' => (string)$memberInfo->ulid,
                        'user_data' => $memberInfo->user_data,
                        'member_type' => $memberInfo->type,
                        'join_at' => $memberInfo->join_at,
                        'account' => $memberInfo->UserBind->account ?? '',
                        'avatar' => $memberInfo->UserBind->avatar ?? '',
                    ],
                ]
            ]);

            // 房主变更通知（加入房间才会触发）
            if (!$createRoom && $joinRoomPosition === 1) {
                Gateway::sendToGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid), [
                    'scene' => 'voice-chat',
                    'event' => 'group-modify-owner',
                    'data' => [
                        'user' => $memberInfo->user,
                        'member' => (string)$memberInfo->ulid,
                        'last_owner' => $roomOwnerInfo->ulid ?? '',
                    ]
                ]);

                if ($roomOwnerInfo) {
                    // 取消房主通知
                    Gateway::sendToUid($roomOwnerInfo->user, [
                        'scene' => 'voice-chat',
                        'event' => 'member-cancel-owner',
                    ]);
                }
            }

            Gateway::sendToClient($client_id, [
                'scene' => 'voice-chat',
                'event' => 'client-join-room',
                'data' => [
                    'room' => $roomInfo->ulid,
                    'member' => (string)$memberInfo->ulid,
                    'channel' => VoiceChat::getInstance()->getRoomName($roomInfo->ulid),
                    'token' => $token,
                ]
            ]);
        }

        // 房间详情
        if ($data['event'] === 'room-detail') {
            Gateway::sendToClient($client_id, [
                'scene' => 'voice-chat',
                'event' => 'client-room-detail',
                'data' => [
                    'ulid' => $roomInfo->ulid,
                    'gag_type' => $roomInfo->gag_type,
                    'member_count' => Gateway::getClientCountByGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid)),
                    'created_at' => date("Y-m-d H:i:s", strtotime($roomInfo->created_at)),
                ]
            ]);
        }

        // 开始讲话
        if ($data['event'] === 'start-talking') {
            Gateway::sendToGroup($this->getRoomName($roomInfo->ulid), [
                'scene' => 'voice-chat',
                'event' => 'group-start-talking',
                'data' => [
                    'user' => $voiceChatUserInfo->user,
                    'member' => (string)$voiceChatUserInfo->ulid,
                ]
            ]);
        }

        // 结束讲话
        if ($data['event'] === 'end-talked') {
            Gateway::sendToGroup($this->getRoomName($roomInfo->ulid), [
                'scene' => 'voice-chat',
                'event' => 'group-end-talked',
                'data' => [
                    'user' => $voiceChatUserInfo->user,
                    'member' => (string)$voiceChatUserInfo->ulid,
                ]
            ]);
        }

        // 房间成员列表
        if ($data['event'] === 'room-member-list') {
            $noPage = max(1, $data['data']['no_page'] ?? 1);
            $pageSize = max(1, $data['data']['page_size'] ?? 10);

            $listSql = VoiceChatUser::query()->where('room', $roomInfo->ulid)->where('state', 1)->whereIn('user', array_values($onlineUserList))
                ->oldest('type')->latest('created_at');

            $list = (clone $listSql)->skip(($noPage - 1) * $pageSize)->take($pageSize)->get()->makeHidden([
                'id', 'UserBind', 'room', 'user', 'state', 'created_at', 'updated_at', 'deleted_at',
            ])->map(function ($row) {
                $row['member_type'] = $row->type;
                $row['account'] = $row->UserBind->account ?? '';
                $row['avatar'] = $row->UserBind->avatar ?? '';

                return $row;
            })->toArray();

            Gateway::sendToClient($client_id, [
                'scene' => 'voice-chat',
                'event' => 'client-room-member-list',
                'data' => [
                    'list' => $list,
                    'pagination' => [
                        'no_page' => $noPage,
                        'page_size' => $pageSize,
                        'total_page' => ceil((clone $listSql)->count() / $pageSize),
                        'total_count' => (clone $listSql)->count(),
                    ]
                ]
            ]);
        }

        // 修改房主
        if ($data['event'] === 'modify-owner') {
            if ($voiceChatUserInfo->type !== 1) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '没有操作权限',
                    ]
                ]);
                return;
            }

            if ($data['data']['new_owner'] === $voiceChatUserInfo->ulid) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '不能操作自己',
                    ]
                ]);
                return;
            }

            $newOwnerInfo = VoiceChatUser::query()->where('room', $roomInfo->ulid)->where('ulid', $data['data']['new_owner'])->where('state', 1)->first();
            if (!$newOwnerInfo) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '当前操作的成员已离开房间',
                    ]
                ]);
                return;
            }

            $newOwnerInfo->update([
                'type' => 1
            ]);
            $voiceChatUserInfo->update([
                'type' => 3,
            ]);

            // 房主变更通知
            Gateway::sendToGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid), [
                'scene' => 'voice-chat',
                'event' => 'group-modify-owner',
                'data' => [
                    'user' => $newOwnerInfo->user,
                    'member' => (string)$newOwnerInfo->ulid,
                    'last_owner' => $voiceChatUserInfo->ulid ?? '',
                ]
            ]);
            // 取消房主通知
            Gateway::sendToClient($client_id, [
                'scene' => 'voice-chat',
                'event' => 'client-cancel-owner',
            ]);
            // 成为房主通知
            Gateway::sendToUid($newOwnerInfo->user, [
                'scene' => 'voice-chat',
                'event' => 'member-become-owner',
            ]);

        }

        // 管理员操作
        if ($data['event'] === 'admin-operation') {
            if ($voiceChatUserInfo->type !== 1) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '没有操作权限',
                    ]
                ]);
                return;
            }

            if ($data['data']['admin_user'] === $voiceChatUserInfo->ulid) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '不能操作自己',
                    ]
                ]);
                return;
            }

            $adminUserInfo = VoiceChatUser::query()->where('room', $roomInfo->ulid)->where('ulid', $data['data']['admin_user'])->where('state', 1)->first();
            if (!$adminUserInfo) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '当前操作的成员已离开房间',
                    ]
                ]);
                return;
            }

            if ($adminUserInfo->type == 1) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '不能操作房主',
                    ]
                ]);
                return;
            }

            if ($data['data']['operation_type'] == 1) {
                if ($adminUserInfo->type == 2) {
                    Gateway::sendToClient($client_id, [
                        'scene' => 'voice-chat',
                        'event' => 'client-error-tip',
                        'data' => [
                            'message' => '该成员已是管理员',
                        ]
                    ]);
                    return;
                }

                $adminUserInfo->update([
                    'type' => 2
                ]);

                // 设立管理员通知
                Gateway::sendToGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid), [
                    'scene' => 'voice-chat',
                    'event' => 'group-setup-admin',
                    'data' => [
                        'user' => $adminUserInfo->user,
                        'member' => (string)$adminUserInfo->ulid,
                    ]
                ]);
                // 成为管理员通知
                Gateway::sendToUid($adminUserInfo->user, [
                    'scene' => 'voice-chat',
                    'event' => 'member-setup-admin',
                ]);
            } else if ($data['data']['operation_type'] == 2) {
                if ($adminUserInfo->type == 3) {
                    Gateway::sendToClient($client_id, [
                        'scene' => 'voice-chat',
                        'event' => 'client-error-tip',
                        'data' => [
                            'message' => '该成员已是普通成员',
                        ]
                    ]);
                    return;
                }

                $adminUserInfo->update([
                    'type' => 3
                ]);

                // 取消管理员通知
                Gateway::sendToGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid), [
                    'scene' => 'voice-chat',
                    'event' => 'group-cancel-admin',
                    'data' => [
                        'user' => $adminUserInfo->user,
                        'member' => (string)$adminUserInfo->ulid,
                    ]
                ]);
                // 被取消管理员通知
                Gateway::sendToUid($adminUserInfo->user, [
                    'scene' => 'voice-chat',
                    'event' => 'member-cancel-admin',
                ]);
            } else {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '操作类型错误',
                    ]
                ]);
            }
        }

        // 修改禁言类型
        if ($data['event'] === 'modify-gag-type') {
            if (!in_array($data['data']['gag_type'], ['0', '1', '2'])) {  // 0不禁言 1全体禁言 2普通成员禁言
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '操作类型错误',
                    ]
                ]);
                return;
            }

            if ($voiceChatUserInfo->type !== 1) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '没有操作权限',
                    ]
                ]);
                return;
            }

            $roomInfo->update([
                'gag_type' => (int)$data['data']['gag_type'],
            ]);

            // 修改禁言类型通知
            Gateway::sendToGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid), [
                'scene' => 'voice-chat',
                'event' => 'group-modify-gag-type',
                'data' => [
                    'type' => (int)$data['data']['gag_type']
                ]
            ]);
        }

        // 踢出房间
        if ($data['event'] === 'kick-member') {
            if (!in_array($voiceChatUserInfo->type, [1, 2])) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '没有操作权限',
                    ]
                ]);
                return;
            }

            if ($data['data']['kick_user'] === $voiceChatUserInfo->ulid) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '不能操作自己',
                    ]
                ]);
                return;
            }

            $kickUserInfo = VoiceChatUser::query()->where('room', $roomInfo->ulid)->where('ulid', $data['data']['kick_user'])->where('state', 1)->first();
            if (!$kickUserInfo) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '当前操作的成员已离开房间',
                    ]
                ]);
                return;
            }

            // 不能踢房主
            if ($kickUserInfo->type === 1) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '不能将房主踢出房间',
                    ]
                ]);
                return;
            }

            // 管理员不能踢管理员
            if ($voiceChatUserInfo->type === 2 && $kickUserInfo->type === 2) {
                Gateway::sendToClient($client_id, [
                    'scene' => 'voice-chat',
                    'event' => 'client-error-tip',
                    'data' => [
                        'message' => '无操作权限',
                    ]
                ]);
                return;
            }

            // 踢出房间通知
            Gateway::sendToGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid), [
                'scene' => 'voice-chat',
                'event' => 'group-kick-member',
                'data' => [
                    'user' => $kickUserInfo->user,
                    'member' => (string)$kickUserInfo->ulid,
                ]
            ]);
            // 被踢出通知
            Gateway::sendToUid($kickUserInfo->user, [
                'scene' => 'voice-chat',
                'event' => 'member-kick',
            ]);
            Gateway::leaveGroup($kickUserInfo->user, VoiceChat::getInstance()->getRoomName($roomInfo->ulid));

        }

        // 离开房间
        if ($data['event'] === 'leave-room') {
            Gateway::leaveGroup($client_id, VoiceChat::getInstance()->getRoomName($roomInfo->ulid));
            // 离开房间通知
            Gateway::sendToGroup(VoiceChat::getInstance()->getRoomName($roomInfo->ulid), [
                'scene' => 'voice-chat',
                'event' => 'group-leave-room',
                'data' => [
                    'user' => $voiceChatUserInfo->user,
                    'member' => (string)$voiceChatUserInfo->ulid,
                ]
            ]);
            $voiceChatUserInfo->update([
                'state' => 0,  // 离开房间
            ]);

            if ($voiceChatUserInfo->type === 1) {
                // 更新房主
                $this->firstOnlineMemberToBeOwner($roomInfo->ulid, (string)$voiceChatUserInfo->ulid);
            }
        }

        // 检查更新房主
        $this->firstOnlineMemberToBeOwner($roomInfo->ulid);

    }


    /**
     * 关闭连接
     *
     * @param $client_id
     * @return void
     */
    public function OnClose($client_id): void
    {

        $userId = Redis::get('BIQI_GAME:websocket:client_id:' . $client_id);

        if (!empty($userId)) {
            $memberList = VoiceChatUser::query()->where('user', $userId)->where('state', 1)->get();
            foreach ($memberList as $member) {
                Gateway::leaveGroup($client_id, $this->getRoomName($member->room));
                // 离开房间通知
                Gateway::sendToGroup($this->getRoomName($member->room), [
                    'scene' => 'voice-chat',
                    'event' => 'group-leave-room',
                    'data' => [
                        'user' => $member->user,
                        'member' => (string)$member->ulid,
                    ]
                ]);
                // 如果是房主
                if ($member->type === 1) {
                    // 更新房主
                    $this->firstOnlineMemberToBeOwner($member->room, (string)$member->ulid);
                }
                $this->firstOnlineMemberToBeOwner($member->room);
                $member->update([
                    'state' => 0,
                ]);
            }
        }

    }


    /**
     * 获取房间名
     *
     * @param $ulid
     * @return string
     */
    public function getRoomName($ulid): string
    {

        return 'voice_chat_room_' . $ulid;

    }

}